package com.jfl.core.config;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.google.common.collect.Maps;
import com.jfl.core.filter.CaptchaValidateFilter;
import com.jfl.core.filter.JwtFilter;
import com.jfl.core.shiro.CustomRealm;
import com.jfl.filter.LogoutFilter;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/**
 * shiro配置
 * 
 * @author 赵炎
 * @version [V1.00, 2018年12月5日]
 * @since V1.00
 */
@Configuration
public class ShiroConfig
{
    public static final String PREMISSION_STRING = "perms[\"{0}\"]";
    
    /**
     * Session超时时间，单位为毫秒（默认30分钟）
     */
    @Value("${jfl.shiro.session.expireTime}")
    private int expireTime;
    
    /**
     * 相隔多久检查一次session的有效性，单位毫秒，默认就是10分钟
     */
    @Value("${jfl.shiro.session.validationInterval}")
    private int validationInterval;
    
    /**
     * 验证码开关
     */
    @Value("${jfl.shiro.user.captchaEnabled}")
    private boolean captchaEnabled;
    
    /**
     * 验证码类型
     */
    @Value("${jfl.shiro.user.captchaType}")
    private String captchaType;
    
    /**
     * 设置Cookie的域名
     */
    @Value("${jfl.shiro.cookie.domain}")
    private String domain;
    
    /**
     * 设置cookie的有效访问路径
     */
    @Value("${jfl.shiro.cookie.path}")
    private String path;
    
    /**
     * 设置HttpOnly属性
     */
    @Value("${jfl.shiro.cookie.httpOnly}")
    private boolean httpOnly;
    
    /**
     * 设置Cookie的过期时间，秒为单位
     */
    @Value("${jfl.shiro.cookie.maxAge}")
    private int maxAge;
    
    /**
     * 登录地址
     */
    @Value("${jfl.shiro.user.loginUrl}")
    private String loginUrl;
    
    /**
     * 权限认证失败地址
     */
    @Value("${jfl.shiro.user.unauthorizedUrl}")
    private String unauthorizedUrl;
    
    /**
     * 缓存前缀
     */
    @Value("${jfl.shiro.cache.prefix}")
    private String keyPrefix;
    
    public LogoutFilter logoutFilter()
    {
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl(loginUrl);
        return logoutFilter;
    }
    
    /**
     * 安全管理器
     * 
     * @param customRealm
     * @return
     */
    @Bean
    public SecurityManager securityManager(CustomRealm customRealm)
    {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm
        securityManager.setRealm(customRealm);
        // 记住我
        securityManager.setRememberMeManager(rememberMeManager());
        // 注入缓存管理器 使用redis缓存
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理器
        securityManager.setSessionManager(sessionManager());
        //SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }
    
    /**
     * Shiro session 管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager()
    {

        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        // 删除过期的session
        defaultWebSessionManager.setDeleteInvalidSessions(true);
        // session 超时时间默认30分钟
        defaultWebSessionManager.setGlobalSessionTimeout(expireTime * 60 * 1000);
        // 去除shiro登录时URL里的JSESSION
        defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);
        // 是否定时检查session
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
        // 校验session间隔10s
        defaultWebSessionManager.setSessionValidationInterval(10* 1000);
        defaultWebSessionManager.setCacheManager(cacheManager());
        // 自定义SessionDao
        defaultWebSessionManager.setSessionDAO(redisSessionDAO());
        return defaultWebSessionManager;
    }
    
    /**
     * RememberMe管理器
     * 
     * @return
     */
    public CookieRememberMeManager rememberMeManager()
    {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));
        return cookieRememberMeManager;
    }
    
    private Cookie rememberMeCookie()
    {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setDomain(domain);
        cookie.setPath(path);
        cookie.setHttpOnly(httpOnly);
        // 一个月
        cookie.setMaxAge(expireTime * 24 * 60 * 60* 1000);
        return cookie;
    }
    
    /**
     * thymeleaf模板引擎和shiro框架的整合
     */
    @Bean
    public ShiroDialect shiroDialect()
    {
        return new ShiroDialect();
    }
    
    /**
     * 开启Shiro的注解(如 @RequiresRoles, @RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * 
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator()
    {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    
    /**
     * 开启Shiro注解通知器
     * 使用代理方式;所以需要开启代码支持;
     * 
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
    /**
     * 自定义验证码过滤器
     */
    @Bean
    public CaptchaValidateFilter captchaValidateFilter()
    {
        CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
        captchaValidateFilter.setCaptchaEnabled(captchaEnabled);
        captchaValidateFilter.setCaptchaType(captchaType);
        return captchaValidateFilter;
    }
    
    /**
     * Shiro密码匹配器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher()
    {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(1);
        return hashedCredentialsMatcher;
    }
    
    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     * 
     * @return
     */
    @Bean
    @Primary
    public CustomRedisManager customRedisManager()
    {
        CustomRedisManager redisManager = new CustomRedisManager();
        return redisManager;
    }
    
    /**
     * cacheManager缓存 redis实现
     * 权限
     * 使用的是shiro-redis开源插件
     * 
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager()
    {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(customRedisManager());
        redisCacheManager.setExpire(expireTime * 60 * 1000);
        redisCacheManager.setKeyPrefix(keyPrefix);
        return redisCacheManager;
    }
    
    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO()
    {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setKeyPrefix(keyPrefix);
        redisSessionDAO.setExpire(expireTime * 10 * 1000);
        redisSessionDAO.setRedisManager(customRedisManager());
        return redisSessionDAO;
    }
    
    /**
     * Shiro过滤器配置
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败，则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        // 权限认证失败，则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        // Shiro连接约束配置，即过滤链的定义
        LinkedHashMap<String, String> filterChainDefinitionMap = Maps.newLinkedHashMap();
        // 对静态资源设置匿名访问 权限控制'
        // anon AnonymousFilter
        //
        filterChainDefinitionMap.put("/favicon.ico**", "anon");
        filterChainDefinitionMap.put("/jfl.png**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/docs/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/profile/**", "anon");
        filterChainDefinitionMap.put("/ajax/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/jfl/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        // filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
        // 配置所有请求都必须认证
        // filterChainDefinitionMap.put("/**", "authc");
        // 退出 logout地址，shiro去清除session LogoutFilter
        filterChainDefinitionMap.put("/logout", "logout");
        // 不需要拦截的访问
        filterChainDefinitionMap.put("/login", "anon,captchaValidate");
        // 系统权限列表
        filterChainDefinitionMap.put("/**", "user");
        // filterChainDefinitionMap.put("/**", "jwt");
        
        // 自定义拦截器
        Map<String, Filter> filters = Maps.newLinkedHashMap();
        filters.put("captchaValidate", captchaValidateFilter());
        filters.put("logout", logoutFilter());
        
        filters.put("jwt", new JwtFilter());
        
        shiroFilterFactoryBean.setFilters(filters);
        
        // 所有请求需要认证
        filterChainDefinitionMap.put("/**","user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        
        return shiroFilterFactoryBean;
    }
    @Bean
    public FilterRegistrationBean<Filter> delegatingFilterProxy()
    {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }
    
}
